{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Three qubit repetition code\n",
        "This sample demonstrates how to implement a 3 qubit repetition code that can be used to detect and correct bit flip errors. It leverages integrated hybrid computing features to count the number of times a bit flip occurred while qubits are coherent.\n",
        "\n",
        "Find more about integrated quantum computing at https://aka.ms/AQ/Hybrid/Docs\n",
        "\n",
        "In this sample we will:\n",
        "1. Implement a 3 qubit repetition code.\n",
        "2. Detect and correct bit flip errors.\n",
        "3. Use classical compute to count the number of times a bit flip occurred.\n",
        "## Connect to the Azure Quantum workspace\n",
        "First, find the resource ID of your Azure Quantum workspace. You can copy/paste this from the top-right corner of your Quantum Workspace page in Azure Portal.\n",
        "\n",
        "Next, you can run `%azure.connect` to connect to the workspace and display the list of provisioned targets that support execution of Q# programs. If you are prompted to login, be sure to use the same account you used to create your Azure Quantum workspace."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.connect \"/subscriptions/subscriptionId/resourceGroups/resourceGroupName/providers/Microsoft.Quantum/Workspaces/workspaceName\" location=\"westus\""
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Select a target and opt-in to use integrated hybrid computing\n",
        "Now, we select the target the program is intended to be run on using the `%azure.target` command. This information is used by the Q# compiler to check the program can be run on the specified target taking into consideration its capabilities.\n",
        "\n",
        "Since integrated hybrid computing is an opt-in feature, we run the `%azure.target-capability` command to opt into it.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%azure.target quantinuum.sim.h1-1e\n",
        "%azure.target-capability AdaptiveExecution"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Open the required Q# namespaces\n",
        "Open the Q# namespaces that provide the necessary operations to implement this sample.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "open Microsoft.Quantum.Intrinsic;\n",
        "open Microsoft.Quantum.Math;\n",
        "open Microsoft.Quantum.Measurement;"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Implement a repetition code\n",
        "The following operation is used to create a repetition code using a 3 qubit register."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "operation Encode(register : Qubit[]) : Unit is Adj {\n",
        "    CNOT(register[0], register[1]);\n",
        "    CNOT(register[0], register[2]);\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Measure a bit flip error syndrome\n",
        "The following operation measures a bit flip error syndrome by checking the parities between qubits 0 and 1, and between qubits 1 and 2.\n",
        "\n",
        "This operation uses a 2 qubit auxiliary register and leverages two integrated hybrid computing capabilities: mid-program qubit measurement and qubit re-use. For some programs, these features reduce the number of qubits needed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "operation MeasureBitFlipSyndrome(encodedRegister : Qubit[], auxiliaryRegister : Qubit[]) : (Result, Result)\n",
        "{\n",
        "    // Measure the bit flip syndrome by checking the parities between qubits 0 and 1, and between qubits 1 and 2.\n",
        "    ResetAll(auxiliaryRegister);\n",
        "    CNOT(encodedRegister[0], auxiliaryRegister[0]);\n",
        "    CNOT(encodedRegister[1], auxiliaryRegister[0]);\n",
        "    CNOT(encodedRegister[1], auxiliaryRegister[1]);\n",
        "    CNOT(encodedRegister[2], auxiliaryRegister[1]);\n",
        "    let parity01 = MResetZ(auxiliaryRegister[0]);\n",
        "    let parity12 = MResetZ(auxiliaryRegister[1]);\n",
        "    return (parity01, parity12);\n",
        "}\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Use the bit flip error syndrome to revert bit flips\n",
        "The following operation reverts a bit flip in a specific qubit in the register depending on the provided error syndrome. It returns a boolean that represents whether any a bit flip was reverted on any of the qubits\n.",
        "\n",
        "This operation uses another integrated hybrid computing capability, branching based on qubit measurements. As we can observe, a different quantum operation is performed depending on the measurement results received as parameters."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "operation RevertBitFlip(register : Qubit[], parity01 : Result, parity12 : Result) : Bool\n",
        "{\n",
        "    if (parity01 == One and parity12 == Zero) {\n",
        "        X(register[0]);\n",
        "    }\n",
        "    elif (parity01 == One and parity12 == One) {\n",
        "        X(register[1]);\n",
        "    }\n",
        "    elif (parity01 == Zero and parity12 == One) {\n",
        "        X(register[2]);\n",
        "    }\n",
        "\n",
        "    return parity01 == One or parity12 == One;\n",
        "}\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Implement an operation that could introduce bit flips\n",
        "In order to test our repetition code, we can apply an operation to our encoded qubit register that is equivalent to applying an identity operation.\n",
        "To achieve this, we can apply a few rotations about the x-axis such that after applying them the qubit register is in the same state it was originally in.\n",
        "Since the `Rx` operation has a $2\\pi$ period, applying a $\\frac{\\pi}{2}$ rotation 4 times should return the qubit to its original state (given that it is not possible to measure the difference between states $|\\psi〉$ and $-|\\psi〉$).\n",
        "Note that since the qubit register represents a repetition code, rotations are applied to each qubit in the register.\n",
        "\n",
        "When running this operation on real quantum devices or noisy simulators, unitended bit flips could occur, and we want our program to detect and correct them.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "operation ApplyRotationalIdentity(register : Qubit[]) : Unit is Adj\n",
        "{\n",
        "    let theta = PI() * 0.5;\n",
        "    for i in 1 .. 4 {\n",
        "        for qubit in register\n",
        "        {\n",
        "            Rx(theta, qubit);\n",
        "        }\n",
        "    }\n",
        "}\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Create a program to test our repetition code\n",
        "Now that we have implemented all the building blocks, we will integrate them all into a single program that we can submit to a quantum device or simulator.\n",
        "\n",
        "Our program will do the following:\n",
        "1. Initialize a qubit into a superposition state $|-〉$.\n",
        "2. Create 3 qubit repetition code.\n",
        "3. Apply several quantum operations to the encoded qubit register, performing bit flip detection and correction between each quantum operation.\n",
        "4. Decode the qubit register and measure it in the computational basis.\n",
        "5. As output, return a boolean representing whether the qubit measurement was the expected one and an integer representing the number of times a bit flip occurred.\n",
        "\n",
        "This program uses two more integrated hybrid computing features: classical compute during coherence time and additional output data types (boolean and integer).\n",
        "### ❕ Please note that you will see a warning displayed (QS5028)\n",
        "It can be ignored for the purposes of this sample. It will be addressed in a subsequent release of the Quantum Development Kit."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "operation ThreeQubitRepetitionCode() : (Bool, Int) {\n",
        "    // Use two qubit registers, one for encoding and an auxiliary one for syndrome measurements.\n",
        "    use encodedRegister = Qubit[3];\n",
        "    use auxiliaryRegister = Qubit[2];\n",
        "\n",
        "    // Initialize the first qubit in the register to a |-〉 state.\n",
        "    H(encodedRegister[0]);\n",
        "    Z(encodedRegister[0]);\n",
        "\n",
        "    // Apply several unitary operations to the encoded qubits performing bit flip detection and correction between\n",
        "    // each application.\n",
        "    mutable bitFlipCount = 0;\n",
        "    within {\n",
        "        // The 3 qubit register is used as a repetition code.\n",
        "        Encode(encodedRegister);\n",
        "    }\n",
        "    apply {\n",
        "        let iterations = 5;\n",
        "        for _ in 1 .. iterations {\n",
        "            // Apply a sequence of rotations to the encoded register that effectively perform an identity operation.\n",
        "            ApplyRotationalIdentity(encodedRegister);\n",
        "\n",
        "            // Measure the bit flip error syndrome, revert the bit flip if needed, and increase the count if a bit flip occurred.\n",
        "            let (parity01, parity12) = MeasureBitFlipSyndrome(encodedRegister, auxiliaryRegister);\n",
        "            let bitFlipReverted = RevertBitFlip(encodedRegister, parity01, parity12);\n",
        "            if (bitFlipReverted) {\n",
        "                set bitFlipCount += 1;\n",
        "            }\n",
        "        }\n",
        "    }\n",
        "\n",
        "    // Transform the qubit to the |1〉 state and measure it in the computational basis.\n",
        "    H(encodedRegister[0]);\n",
        "    let result = MResetZ(encodedRegister[0]) == One;\n",
        "    ResetAll(encodedRegister);\n",
        "\n",
        "    // The output of the program is a boolean-integer tuple where the boolean represents whether the qubit\n",
        "    // measurement result was the expected one and the integer represents the number of times bit flips occurred\n",
        "    // throughout the program.\n",
        "    return (result, bitFlipCount);\n",
        "}\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Submit the three qubit repetition code program\n",
        "### ❕ This program will use approximately 11.31 units (HQCs or eHQCs) using 50 shots, as configured below.\n",
        "If the job has not been completed after the client times out (300 seconds), you can query the status using `%azure.status` followed by querying the output using `%azure.output`.<br/>\n",
        "\n",
        "If the kernel was restarted after the program had been submitted, you will have to use `%azure.status <job_id>` to get the status of the job and `%azure.output <job_id>` to retrieve the results.<br/>\n",
        "You can also check the status of the job under the **Job management** section on the left side menu of the portal.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%azure.execute ThreeQubitRepetitionCode shots=50 timeout=300 poll=10"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.status"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Understanding the results\n",
        "Each item in the histogram is a boolean-integer tuple representing whether the qubit measurement was the expected one and the number of times bit flip occurred throughout program execution. Associated to an item in the histogram is the frequency of that particular output.<br/>\n",
        "\n",
        "For example:\n",
        "- A `(true, 0)` output means that the qubit was measured in the expected state and no bit flips occurred.\n",
        "- A `(true, 2)` output means that the qubit was measured in the expected state and bit flips occurred twice.\n",
        "- A `(false, 1)` output means that the qubit was **not** measured in the expected state and bit flips occurred once.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "%azure.output"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## ↗ Learning more about Integrated Hybrid Computing\n",
        "Now that you have run this sample, you can further learn and experiment with other samples available in the gallery.\n",
        "\n",
        "To learn more, please refer to our [online documentation](https://aka.ms/AQ/Hybrid/Docs)."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
     "display_name": "Q#",
     "language": "qsharp",
     "name": "iqsharp"
    },
    "language_info": {
     "file_extension": ".qs",
     "mimetype": "text/x-qsharp",
     "name": "qsharp",
     "version": "0.14"
    }
   },
  "nbformat": 4,
  "nbformat_minor": 0
}
